/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.gc.cscott.cli;

import com.gc.cscott.domain.contest.Contest;
import com.gc.cscott.domain.contest.Task;
import com.gc.cscott.domain.contest.TestCase;
import com.gc.cscott.domain.result.CompetitorResult;
import com.gc.cscott.domain.result.TestCaseResult;
import com.gc.cscott.fs.FileSystemWorker;
import com.gc.cscott.store.ResultsStorage;
import com.gc.cscott.domain.Competitor;
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
import java.awt.Desktop;
import java.io.ByteArrayOutputStream;
import java.io.File;
import org.apache.commons.lang.StringUtils;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

/**
 * Report results of a competitors in html
 * @author andrey.stukalin
 */
public class ReportPersonalResultsHtmlCommand implements Command {

    @Inject
    Contest contest;
    @Inject
    FileSystemWorker fsWorker;
    @Inject
    ResultsStorage resultsStorage;

    public List<String> listHelp() {
        return Arrays.asList(
                "Report results of a competitors in html. Options:",
                " <name> - any word to find competitor by name ",
                " OR ",
                " all - get results of all competitors");
    }

    public List<String> perform(List<String> args) {
        if (args.isEmpty()) {
            throw new Error("No parameters was specified");
        } else {
            try {
                LinkedList<String> mutableArgs = new LinkedList<String>(args);
                final String likeName = mutableArgs.removeFirst();
                final boolean modeAllResults = likeName.equals("all");

                if (!modeAllResults) {
                    generateByName(likeName);
                } else {
                    generateAll();
                }
                return ImmutableList.copyOf(mutableArgs);
            } catch (IOException e) {
                throw new Error(e);
            }
        }
    }

    private File writeReport(Competitor competitor) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        final PrintStream output = new PrintStream(baos);
        writeHeader(output,competitor);
        output.println("<div align='center'><h1>" + competitor.getName() + "</h1></div>");
        writeSummary(competitor, output);
        output.println("</body></html>");
        output.close();
        File reportFile = resultsStorage.storeReport(competitor, new String(baos.toByteArray()), "html");
        System.out.println("Report stored in: "+ reportFile.getAbsolutePath());
        return reportFile;
    }

    private void writeHeader(final PrintStream output, final Competitor competitor) {
        output.println("<html><head><title>");
        output.println("Results | "+competitor.getName());
        output.println("</title>");
        output.println("<style type='text/css'>");
        output.println("body {font-family: 'Courier',monospace; font-size: 9px}");
        output.println("h1 {font-family: 'Courier',monospace; font-size: 24px}");
        output.println("table  {border-collapse: collapse; border: 3px solid #FFFFFF}");
        output.println("table td {border-bottom:1px solid;border-right:1px solid; border-top: 1px solid #FFFFFF; padding:5px;  font-family: 'Courier',monospace; font-size: 9px");
        output.println("</style>");
        output.println("</head><body>");
    }

    private void generateAll()throws IOException {
        List<Competitor> competitorsList = (fsWorker.listCompetitorsAll());
        if (competitorsList.isEmpty()) {
            throw new Error("No competitors found.");
        } else {
            for (Competitor competitor : competitorsList) {
                writeReport(competitor);
            } 
        }
    }

    private void generateByName(final String likeName) throws IOException {

        List<Competitor> competitorsList = fsWorker.listCompetitorsAll();
        if (competitorsList.isEmpty()) {
            throw new Error("No competitors found.");
        } else {
            Competitor nearest = Collections.min(competitorsList, new Comparator<Competitor>() {
                public int compare(Competitor left, Competitor right) {
                    int leftDistance = StringUtils.getLevenshteinDistance(left.getName(), likeName);
                    int rightDistance = StringUtils.getLevenshteinDistance(right.getName(), likeName);
                    return leftDistance - rightDistance;
                }
            });
            final int nearestDistance = StringUtils.getLevenshteinDistance(nearest.getName(), likeName);
            int countOfNearestCompetitors = 0;

            for (Competitor competitor : competitorsList) {
                final int competitorDistance = StringUtils.getLevenshteinDistance(competitor.getName(), likeName);
                if (nearestDistance == competitorDistance) {
                    countOfNearestCompetitors++;
                }
            }
            if (countOfNearestCompetitors == 1) {
                File reportFile = writeReport(nearest);
                Desktop.getDesktop().open(reportFile);
            } else {
                throw new Error("Please specify a name more precisely.");
            }
        }
    }

    private void writeSummary(Competitor competitor, final PrintStream output) throws IOException {
        CompetitorResult result = resultsStorage.loadResults(competitor);
        int totalPassed = 0;
        int totalFailed = 0;
        int totalScore = 0;
        for (Task task : contest.getTasksList()) {
            output.println("<table>");
            output.println("<tr>");
            output.println("<th colspan='4' align='left'>");
            output.printf("Task %2d '%s'", task.getNumber().intValue(), task.getName());
            output.println("</th>");
            output.println("</tr>");
            int testPassed = 0;
            int testFailed = 0;
            int testScore = 0;
            for (TestCase testCase : task.getTestCaseList()) {
                TestCaseResult tcr = result.getResult(testCase);
                if (tcr != null) {
                    if (tcr.getResult() == TestCaseResult.HowEnded.Correct) {
                        testPassed++;
                        testScore += testCase.getScore();
                    } else {
                        testFailed++;
                    }
                    output.println("<tr>");
                    output.printf("<td width='70px'>  test %2d</td><td width='80px'> [%5dms]</td><td> %-18s</td><td>%s</td>",
                            tcr.getTestCaseNumber().intValue(),
                            tcr.getPayedTime(),
                            tcr.getResult()+
                            (tcr.getResult()== TestCaseResult.HowEnded.Correct
                            ?String.format(", score +%2d",testCase.getScore())
                            :""),
                            (StringUtils.isBlank(tcr.getHint())?"":"- ")+
                            StringUtils.defaultString(tcr.getHint()).replace("\n", " ").replace("\r", " "));
                    output.println("</tr>");
                } else {
                    output.println("<tr>");
                    output.printf("<td colspan='4'>  test %2d - no solution</td>", testCase.getNumber().intValue());
                    output.println("</tr>");
                }
            }
            totalPassed += testPassed;
            totalFailed += testFailed;
            totalScore += testScore;
            output.println("<tr>");
            output.printf("<td colspan='4'>%20s passed <b>%3d</b>, failed <b>%3d</b>, score <b>%3d</b></td>", "", testPassed, testFailed, testScore);
            output.println("</tr>");
            output.println("</table><br/>");
        }
        output.println("<hr/>");
        output.println("<p>Summary</p>");
        output.printf("%20s PASSED <b>%3d</b>, FAILED <b>%3d</b>, SCORE <b>%3d</b>", "", totalPassed, totalFailed, totalScore);        
    }
}
